﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace GempGame
{
    public class Player
    {
        // Player details
        public string Name { get; set; }

        // Output
        public double Balance { get; set; } //Dinheiro total actual

        //public int ProductionRate { get; set; }

        //public string SoldProductsCount { get; set; }
        //public string InterestedClientsCount { get; set; }
        //public string ComplaintsCount { get; set; }
        //public string MediaReferencesCount { get; set; }

        //Historico de percentagens obtidas pelas opções do jogador
        //public ArrayList PercentageLog { get; set; }

        public double karma { get; set; }
        public int evolution { get; set; }
        public int totalClients { get; set; }
        public int totalProduction { get; set; }

        public List<double> percentageHistory { get; set; }

        public List<double> incomeHistory { get; set; }
        public List<int> interestedClientsHistory { get; set; }
        public List<int> clientsHistory { get; set; }
        public List<double> priceHistory { get; set; }
        public List<int> productionHistory { get; set; }
        public List<List<string>> talksHistory { get; set; }

        // Arvore de escolhas do utilizador
        public Option ChoiceTree { get; set; }
        public Option ChosenMarket { get; set; }
        public Level ChosenLevel { get; set; }
        
        
        public string filename = "IPhoneXML.xml";

        public int week { get; set; }

        public Factory factory { get; set; }
        public Bank bank { get; set; }

        public Player(string name)
        {
            this.Name = name;
            this.karma = 0.0;
            this.evolution = 0;
            this.Balance = 0.0;
            this.week = 1;
            this.totalClients = 0;
            this.totalProduction = 0;
            this.percentageHistory = new List<double>();
            this.incomeHistory = new List<double>();
            this.priceHistory = new List<double>();
            this.interestedClientsHistory = new List<int>();
            this.clientsHistory = new List<int>();
            this.productionHistory = new List<int>();
            this.talksHistory = new List<List<string>>();
        }

        public List<Option> getMarkets()
        {
            return this.ChoiceTree.Options;
        }

        //Permite escolher o mercado
        public Option chooseMarket()
        {
            Console.WriteLine("Escolher mercado: ");
            int j = 0;
            for (; j < this.ChoiceTree.Options.Count; j++)
            {
                Console.WriteLine("     " + j + " - " + this.ChoiceTree.Options[j].Name);
            }
            int opcMarket = -1;
            while (opcMarket < 0 || opcMarket >= this.ChoiceTree.Options.Count)
            {
                Console.Write("Opcao: ");
                opcMarket = int.Parse(Console.ReadLine());
            }

            this.ChoiceTree.Choice = opcMarket;

            ChosenMarket = this.ChoiceTree.Options[opcMarket];

            return this.ChosenMarket;
        }

        public void cleanStrategy()
        {
            this.ChosenMarket.Choice = -1;
            foreach (Option opt in this.ChosenMarket.Options)
                cleanStrategy(opt);
        }

        public void cleanStrategy(Option opt)
        {
            opt.Choice = -1;
            foreach (Option subOpt in opt.Options)
                cleanStrategy(subOpt);
        }

        public void chooseTree()
        {
            Console.WriteLine("\nEscolher Estrategia");

            List<Option> escolhas = new List<Option>();


            escolhas.Add(this.ChosenMarket);

            while (escolhas.Count > 0)
            {
                //Console.WriteLine("ESCOLHAS:::: " + escolhas.Count);
                if (escolhas[0].finalNode == true)
                {
                    //Console.WriteLine("\nFinalNode: true");
                    if (escolhas[0].Options.Count > 0)
                    {
                        escolhas.Add(escolhas[0].chooseOption());
                        escolhas.RemoveAt(0);
                    }
                    else
                    {
                        escolhas.RemoveAt(0);
                    }
                }
                else
                {
                    //Console.WriteLine("\nFinalNode: false");
                    foreach (Option ramo in escolhas[0].Options)
                    {
                        escolhas.Add(ramo);
                    }
                    escolhas.RemoveAt(0);
                }
            }

        }

        public void refreshKarmaWeek()
        {
            if (week > 2)
            {
                //Caso aumente o numero
                if (this.clientsHistory[this.clientsHistory.Count - 1] > this.clientsHistory[this.clientsHistory.Count - 2])
                {
                    if (this.evolution >= 0)
                        this.evolution += 1;
                    else
                        this.evolution = 0;
                }
                else if (this.clientsHistory[this.clientsHistory.Count - 1] < this.clientsHistory[this.clientsHistory.Count - 2])
                {
                    if (this.evolution <= 0)
                        this.evolution -= 1;
                    else
                        this.evolution = 0;
                }

                double val = 0.0;

                int difProduction = (this.clientsHistory[this.clientsHistory.Count - 1] - this.productionHistory[this.productionHistory.Count - 1]);
                
                if (difProduction <= ((this.clientsHistory[this.clientsHistory.Count - 1] * 2) - (this.clientsHistory[this.clientsHistory.Count - 1] * 0.5)))
                    val = (0.7 * this.evolution) + (difProduction * 0.3) - (this.bank.loans.Count * (0.15 * this.evolution));
                else
                    val = (0.7 * this.evolution) - (difProduction * 0.3) - (this.bank.loans.Count * (0.15 * this.evolution));


                Random generatedRand = new Random();
                double randomNumber = 0.0;

                if ((Math.Abs(this.evolution) + Math.Abs(difProduction) - this.bank.loans.Count) != 0) {
                    val = val / (Math.Abs(this.evolution) + Math.Abs(difProduction) - this.bank.loans.Count);
                    if (val > 0)
                    {
                        randomNumber = generatedRand.NextDouble();
                        randomNumber = randomNumber * val;
                    }
                    else
                    {
                        randomNumber = generatedRand.NextDouble();
                        randomNumber = -(randomNumber * val);
                    }

                    if (karma + randomNumber > 1.0)
                        karma = 1.0;
                    else if (karma + randomNumber < -1.0)
                        karma = -1.0;
                    else
                        karma += randomNumber;
                    System.Console.WriteLine("KARMA: " + this.karma + " - " + randomNumber + " - " + this.evolution);
                }
            }
            
        }

        public double getCurrentPercentagem()
        {
            return this.ChosenMarket.generatePercentage();
        }

        public double generateWeekClients()
        {
            //Clientes Interessados
            double interestedClients = 0.0;
            double strategy = this.getCurrentPercentagem();
            Random generatedRand = new Random();

            //Aleatoriedade
            int aleatorio = 0;
            if (this.clientsHistory.Count > 0)
                aleatorio = generatedRand.Next(0, (int)(this.clientsHistory[this.clientsHistory.Count - 1] * 0.10));

            if (generatedRand.Next(0, 1) == 0)
                aleatorio = -aleatorio;
            
            if (this.factory.getLowestUpgrade() != null)
                interestedClients = (0.4*this.week) + aleatorio + this.factory.getLowestUpgrade().maxProd * strategy;
            else
                interestedClients = (0.4*this.week) + aleatorio + this.factory.maxProd * strategy;
            
            //Sorte
            interestedClients += this.karma * (interestedClients * 0.05);

            //Preco
            double dif = Math.Abs(this.factory.advisedPrice - this.factory.actualPrice);
            double ratio = dif / this.factory.advisedPrice;

            if (ratio > 0.05)
            {
                interestedClients -= (interestedClients * ratio);
            }

            this.percentageHistory.Add(strategy);
            this.interestedClientsHistory.Add((int)interestedClients);

            //Clientes
            double clients = 0.0;

            Option marketingOpt = Option.getOptionByName(this.ChosenMarket, "NivelSegmentacao");
            if (marketingOpt != null)
            {
                double marketingPercentage = marketingOpt.choicesPercentage;
                clients = (interestedClients * 0.85) + ((interestedClients * 0.15) * marketingPercentage);
            }
            else
            {
                clients = (interestedClients * 0.85) + ((interestedClients * 0.15) * karma);
            }

            //Verifica se ha stock suficiente
            if (clients > this.factory.stock)
                clients = this.factory.stock;

            this.clientsHistory.Add((int)clients);
            this.factory.stock -= (int)clients;
            this.totalClients += clientsHistory[clientsHistory.Count - 1];

            return clientsHistory[clientsHistory.Count - 1];

            /*Random generatedRand = new Random();
            int randomNumber = generatedRand.Next(-(int)(strategy * 100.0), (int)(strategy * 100.0));
            //Console.WriteLine("RandomNumber: " + randomNumber);
            int randomCentenas = generatedRand.Next(100);
            //Console.WriteLine("RandomCenas: " + randomNumber);
            double weekImp = weekImpact();
            //Console.WriteLine("WeekImpact: " + weekImp);
            clientsHistory.Add((int)((((strategy + (randomNumber / 1000.0)) * 10000.0) + randomCentenas) * weekImp));
            this.factory.stock -= clientsHistory[clientsHistory.Count - 1];
            return clientsHistory[clientsHistory.Count-1];*/

        }

        public int generateWeekProduction()
        {
            double production = 0.0;
            Option productionOpt = Option.getOptionByName(this.ChosenMarket, "Producao");
            if (productionOpt != null)
            {
                double productionPercentage = productionOpt.choicesPercentage;
                production = (this.factory.actualProduction * 0.85) + ((this.factory.actualProduction * 0.15) * productionPercentage);
            }
            else
            {
                production = (this.factory.actualProduction * 0.85) + ((this.factory.actualProduction * 0.15) * karma);
            }

            this.productionHistory.Add((int)production);
            this.factory.stock += (int)production;
            totalProduction += (int)production;
            return (int)production;
        }

        public double calculateWeekIncome()
        {
            double val = (this.clientsHistory[week - 1] * this.factory.actualPrice) - (this.productionHistory[week - 1] * this.factory.productionPrice) - this.factory.fixedCosts;
            incomeHistory.Add(val);
            this.Balance += val;
            return val;
        }

        public List<string> generateWeekTalks()
        {
            this.talksHistory.Add(this.getTalks());
            return this.talksHistory[this.talksHistory.Count - 1];
        }

        /*public double weekImpact()
        {
            double form = Math.Pow((this.week / 4.0), 0.85) - Math.Log(this.week / 6.0);
            Console.WriteLine("form: " + form);
            return (form/(100.0 - form));
        }
        */

        public List<string> getTalks()
        {
            if (this.clientsHistory.Count > 0)
            {
                int generatedNumber = -1;
                Random rand = new Random();
                generatedNumber = rand.Next(this.clientsHistory[this.clientsHistory.Count - 1]);
                return this.ChoiceTree.generateTalks(generatedNumber);
            }
            return new List<string>();
        }

        // Define mercado
        public void setMarket(int index)
        {
            ChoiceTree.Choice = index;
        }

        public Option getMarket()
        {
            if (ChoiceTree.Choice < 0)
                throw new Exception("The market has not been chosen yet!");

            return (Option) ChoiceTree.Options[ChoiceTree.Choice];
        }

        // Define opções do mercado consoante o nodeType
        // Pode definir tipo de 'SegmentationLevel', 'Strategy', 'Production' ou 'Company'
        public void setMarketOptions(NodeType nodeType, int index)
        {
            if (ChoiceTree.Choice < 0) {
                throw new Exception("The market has not been chosen yet!");
            }
            
            Option market = (Option) ChoiceTree.Options[ChoiceTree.Choice];
            foreach( Option opt in market.Options)
            {
                if (opt.Type == nodeType)
                {
                    opt.Choice = index;
                }
            }            
        }

        //Retorna o filho do nó market, do tipo nodeType
        public Option getMarketOptions(NodeType nodeType)
        {
            if (ChoiceTree.Choice < 0)
                throw new Exception("The market has not been chosen yet!");

            Option market = (Option)ChoiceTree.Options[ChoiceTree.Choice];
            foreach (Option opt in market.Options)
            {
                if (opt.Type == nodeType)
                    return opt;
            }
            return null;
        }

        //Defines the player 'luck'
        /*public double getKarma()
        {
            double karmaValue = 0;

            karmaValue += compareWithLast();
            karmaValue += compareWithLog();

            return karmaValue;
        }

        private double compareWithLog()
        {
            if (this.PercentageLog.Count == 0)
                throw new Exception("PercentageLog is empty");

            double karmaValue = 0;

            if (this.PercentageLog.Count == 1)
            {
                // (PercentagemActual - 50%) / 10 
                // Valores (+) se percentagem > 50%
                // Valores (-) se percentagem < 50%
                karmaValue += (((int)this.PercentageLog[0] - 50) / 10);
            }
            else
            {
                ; //TODO
            }

            return karmaValue;
        }

        private double compareWithLast()
        {
            //throw new NotImplementedException();
            int currentPercentage   = (int)this.PercentageLog[this.PercentageLog.Count - 1];
            int lastPercentage      = (int)this.PercentageLog[this.PercentageLog.Count - 2];

            return (currentPercentage - lastPercentage);
        }*/


        //Listas necessarias para mostrar os historicos
        public string[] historyClientsList()
        {
            string[] lista = new string[this.clientsHistory.Count];
            int i = this.clientsHistory.Count-1;
            int j = 1;
            foreach (int num in this.clientsHistory)
            {
                lista[i] = "Semana " + j + "         " + num.ToString();
                i--;
                j++;
            }
            return lista;
        }

        public string[] historyProductionList()
        {
            string[] lista = new string[this.productionHistory.Count];
            int i = this.productionHistory.Count - 1;
            int j = 1;
            foreach (int num in this.productionHistory)
            {
                lista[i] = "Semana " + j + "         " + num.ToString();
                i--;
                j++;
            }
            return lista;
        }

        public string[] historyPriceList()
        {
            string[] lista = new string[this.priceHistory.Count];
            int i = this.priceHistory.Count - 1;
            int j = 1;
            foreach (double num in this.priceHistory)
            {
                lista[i] = "Semana " + j + "         " + num.ToString();
                i--;
                j++;
            }
            return lista;
        }

        public string[] historyIncomeList()
        {
            string[] lista = new string[this.incomeHistory.Count];
            int i = this.incomeHistory.Count - 1;
            int j = 1;
            foreach (double num in this.incomeHistory)
            {
                lista[i] = "Semana " + j + "         " + num.ToString();
                i--;
                j++;
            }
            return lista;
        }

        public string[] historyInterestedClientsList()
        {
            string[] lista = new string[this.interestedClientsHistory.Count];
            int i = this.interestedClientsHistory.Count - 1;
            int j = 1;
            foreach (double num in this.interestedClientsHistory)
            {
                lista[i] = "Semana " + j + "         " + num.ToString();
                i--;
                j++;
            }
            return lista;
        }



        public bool loadGame(string filename)
        {
            //filename = "IPhoneXML.xml";

            List<Option> marketsList = Option.loadGame(filename);

            Option markets = new Option();

            markets.Options = marketsList;
            this.ChoiceTree = markets;
            
            return true;
        }
    }
}
